package com.laity.bQueue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.bQueue.BlockingQueueDemo
 * @Date: 2023年11月28日 21:58
 * @Description: BlockingQueue 阻塞队列
 */

public class BlockingQueueDemo {
    // BlockingQueue并列 List和Set Queue:阻塞队列(BlockingQueue)、非阻塞队列(AbstractQueue)、双端队列(Deque)
    // BlockingQueue是一种先进先出(FIFO)的队列，队列满了之后，再往队列中添加元素，会被阻塞，直到队列有空余位置。
    // BlockingQueue的特点：
    // 1. 队列满了之后，再往队列中添加元素，会被阻塞，直到队列有空余位置。
    // 2. 队列空了之后，再往队列中取元素，会被阻塞，直到队列有元素。
    // 3. BlockingQueue提供了四个方法：
    //    put(E e)：向队列中添加元素，如果队列满了，则调用此方法会被阻塞，直到队列有空余位置。
    //    take()：从队列中取出一个元素，如果队列空了，则调用此方法会被阻塞，直到队列有元素。
    //    poll()：从队列中取出一个元素，如果队列空了，则返回null。
    //    peek()：从队列中取出一个元素，如果队列空了，则返回null。
    // 4. BlockingQueue下有两个实现类：
    //    ArrayBlockingQueue：基于数组实现的有界阻塞队列，此队列按FIFO（先进先出）排序元素，吞吐量通常要高于LinkedList。
    //    LinkedBlockingQueue：基于链表实现的阻塞队列，此队列按FIFO（先进先出）排序元素，吞吐量通常要高于LinkedList。
    // 5. BlockingQueue的应用场景：
    //    生产者消费者模型：生产者线程将产品放入队列，消费者线程从队列中取出产品进行处理。
    //    缓冲区：生产者线程将产品放入队列，消费者线程从队列中取出产品进行处理。
    // 什么时候我们需要使用BlockingQueue呢？
    // 在多线程的时候，A去调用B，B需要调用A，但是A和B是并行的，如果A调用B的时候，B还没有准备好，那么A就会一直等待B准备好，这就是阻塞。
    public static void main(String[] args) {
        // test1();
        // test2();
        // test3();
        test4();
    }

    /*
    抛出异常
     */
    public static void test1() {
        // capacity 指定了队列的大小，如果队列满了，再往队列中添加元素，会被阻塞，直到队列有空余位置。
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(2);
        System.out.println(blockingQueue.add("a"));  // true
        System.out.println(blockingQueue.add("b"));  // true
        // System.out.println(blockingQueue.add("c"));  // Queue full:报错信息中已经提示了"Queue full"，说明队列已满，无法再添加元素。
        System.out.println("=========================================");
        System.out.println(blockingQueue.element()); // 查看队列中第一个元素，如果队列为空，则调用此方法会被阻塞，直到队列有元素。
        System.out.println("=========================================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        // System.out.println(blockingQueue.remove());  // java.util.NoSuchElementException 这个报错信息表示在移除一个元素时，队列中没有元素可供移除。
    }

    /*
    不抛出异常，有返回值
     */
    public static void test2() {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(2);
        System.out.println(blockingQueue.offer("a"));  // true
        System.out.println(blockingQueue.offer("b"));  // true
        System.out.println(blockingQueue.offer("c"));  // false
        System.out.println("=========================================");
        System.out.println(blockingQueue.peek()); // 查看队列中第一个元素，如果队列为空，则返回null。
        System.out.println("=========================================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll()); // null
    }

    /*
    等待、阻塞 (一直阻塞、超时阻塞)
     */
    public static void test3() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);
        try {
            // put方法无返回值
            blockingQueue.put("a");
            blockingQueue.put("b");
            // blockingQueue.put("c"); // 队列已满，一直阻塞
            System.out.println("===========================");
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            // System.out.println(blockingQueue.take()); // 队列为空，一直阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /*
    超时等待
     */
    public static void test4() {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);
        try {
            blockingQueue.offer("a");
            blockingQueue.offer("b");
            blockingQueue.offer("c", 2, TimeUnit.SECONDS);
            System.out.println(blockingQueue.poll(1, TimeUnit.SECONDS)); // 等待1秒，如果队列中有元素，则返回队列中的第一个元素，如果1秒内没有元素，则返回null。
            System.out.println(blockingQueue.poll(1, TimeUnit.SECONDS)); // 等待1秒，如果队列中有元素，则返回队列中的第一个元素，如果1秒内没有元素，则返回null。
            System.out.println(blockingQueue.poll(1, TimeUnit.SECONDS)); // 等待1秒，如果队列中有元素，则返回队列中的第一个元素，如果1秒内没有元素，则返回null。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
